package com.ymttest.database.sharding.config;

import io.shardingjdbc.core.api.ShardingDataSourceFactory;
import io.shardingjdbc.core.api.config.ShardingRuleConfiguration;
import io.shardingjdbc.core.api.config.TableRuleConfiguration;
import io.shardingjdbc.core.api.config.strategy.ComplexShardingStrategyConfiguration;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

import com.alibaba.druid.pool.DruidDataSource;
import com.ymttest.database.sharding.ComplexKeysDatabaseAlgorithm;
import com.ymttest.database.sharding.ComplexKeysTableAlgorithm;

public class DbShardingSourceConfig {

	private static final String DB_NAME = "orderdb";

	private static DbShardingSourceConfig shardingSourceConfig = null;

	private ConnectionConfig connectionConfig = ConnectionConfig.getInstance();

	private DbShardingSourceConfig() {

	}

	public DataSource shardingDataSource() {
		int dbCount = connectionConfig.getShardingDbCount();
		//配置数据库实例个数
		try {
			Map<String, DataSource> dataSourceMap = new HashMap<>();
			for (int n = 1; n <= dbCount; n++) {
				DruidDataSource source = DataSourceUtils.initDataSource();
				source.setDefaultAutoCommit(true);
				source.setDriverClassName(com.mysql.jdbc.Driver.class.getName());

				if (!connectionConfig.getShardingDbUrl().contains("dbserver%s")) {
					//开发环境，数据库数据库没有域名化。各库共用一个固定IP
					source.setUrl(String.format(
							connectionConfig.getShardingDbUrl(),
							connectionConfig.getShardingDbPort() + n, n));
				} else {
					source.setUrl(String.format(
							connectionConfig.getShardingDbUrl(), n,
							connectionConfig.getShardingDbPort() + n, n));
				}
				source.setUsername(connectionConfig.getShardingDbUser());
				source.setPassword(connectionConfig.getShardingDbPassword());
				dataSourceMap.put(DB_NAME + "_" + n, source);
			}

			TableRuleConfiguration rule_Order = new TableRuleConfiguration();
			rule_Order.setLogicTable("order");
			rule_Order.setActualDataNodes(String.format(
					"%s_${1..%s}.order_${0..99}", DB_NAME, dbCount));

			TableRuleConfiguration rule_MainOrder = new TableRuleConfiguration();
			rule_MainOrder.setLogicTable("main_order");
			rule_MainOrder.setActualDataNodes(String.format(
					"%s_${1..%s}.main_order_${0..99}", DB_NAME, dbCount));

			TableRuleConfiguration rule_OrderDetail = new TableRuleConfiguration();
			rule_OrderDetail.setLogicTable("order_detail");
			rule_OrderDetail.setActualDataNodes(String.format(
					"%s_${1..%s}.order_detail_${0..99}", DB_NAME, dbCount));

			TableRuleConfiguration rule_Payment = new TableRuleConfiguration();
			rule_Payment.setLogicTable("payment");
			rule_Payment.setActualDataNodes(String.format(
					"%s_${1..%s}.payment_${0..99}", DB_NAME, dbCount));

			TableRuleConfiguration rule_OrderSyncCmd = new TableRuleConfiguration();
			rule_OrderSyncCmd.setLogicTable("order_sync_cmd");
			rule_OrderSyncCmd.setActualDataNodes(String.format(
					"%s_${1..%s}.order_sync_cmd_${0..99}", DB_NAME, dbCount));

			TableRuleConfiguration rule_OrderBiz = new TableRuleConfiguration();
			rule_OrderBiz.setLogicTable("order_biz");
			rule_OrderBiz.setActualDataNodes(String.format(
					"%s_${1..%s}.order_biz", DB_NAME, dbCount));

			ShardingRuleConfiguration ruleConfig = new ShardingRuleConfiguration();
			//添加规则配置
			ruleConfig.getTableRuleConfigs().add(rule_Order);
			ruleConfig.getTableRuleConfigs().add(rule_MainOrder);
			ruleConfig.getTableRuleConfigs().add(rule_OrderDetail);
			ruleConfig.getTableRuleConfigs().add(rule_Payment);
			ruleConfig.getTableRuleConfigs().add(rule_OrderSyncCmd);
			ruleConfig.getTableRuleConfigs().add(rule_OrderBiz);
			//需要分表中必须有一个字段是属于shardingColumns, 否则将在100张表中操作

			//多字段分库策略
			ruleConfig
					.setDefaultDatabaseShardingStrategyConfig(new ComplexShardingStrategyConfiguration(
							"order_id,main_order_id,payment_id",
							ComplexKeysDatabaseAlgorithm.class.getName()));
			//多字段分表策略
			ruleConfig
					.setDefaultTableShardingStrategyConfig(new ComplexShardingStrategyConfiguration(
							"order_id,main_order_id,payment_id",
							ComplexKeysTableAlgorithm.class.getName()));

			return ShardingDataSourceFactory
					.createDataSource(dataSourceMap, ruleConfig,
							new HashMap<String, Object>(), new Properties());

		} catch (SQLException e) {
			throw new RuntimeException("Failed to create sharding data source",
					e);
		}
	}

	public SqlSessionFactory shardingSessionFactory() throws Exception {

		SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();

		// 获取properties中的对应配置信息
		String mapperLocation = "classpath*:com/ymttest/database/sharding/sqlmap/sharding/**/*.xml";
		//	String configLocation = "classpath*:config/mybatis-settings.xml";

		/*		String configLocation = System.getProperty("user.dir") + File.separator
						+ "config" + File.separator + "mybatis-settings.xml";*/

		Properties properties = new Properties();

		sessionFactory.setDataSource(shardingDataSource());
		sessionFactory.setConfigurationProperties(properties);

		// 设置MapperLocations configLocation路径
		ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

		/*	System.out.println("mapperLocation size:"
					+ resourcePatternResolver.getResources(mapperLocation).length);
		*/
		sessionFactory.setMapperLocations(resourcePatternResolver
				.getResources(mapperLocation));

		/*		sessionFactory.setConfigLocation(resourcePatternResolver
						.getResource(configLocation));*/

		return sessionFactory.getObject();
	}
	public SqlSessionTemplate shardingSessionTemplate() throws Exception {
		return new SqlSessionTemplate(shardingSessionFactory());
	}

	public PlatformTransactionManager shardingTransactionManager() {
		return new DataSourceTransactionManager(shardingDataSource());
	}

	public TransactionTemplate shardingTransactionTemplate() {
		return new TransactionTemplate(shardingTransactionManager());
	}

	public static DbShardingSourceConfig getInstance() {

		if (shardingSourceConfig == null) {

			shardingSourceConfig = new DbShardingSourceConfig();

		}

		return shardingSourceConfig;
	}
}
